package org.noahsark.core.dao;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.EntityManager;

import org.hibernate.Session;
import org.noahsark.core.domainmodel.BaseEntity;
import org.noahsark.core.context.UserInfo;
import org.noahsark.utilities.common.SimpleBeanCopyUtil;
import org.noahsark.utilities.common.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * 提供实体的增删改和全表查询.
 * 
 * @param <E> E
 * @param <K> Serializable
 */
public abstract class BaseDao<E extends BaseEntity<K>, K extends Serializable> {

    protected final Log log = LogFactory.getLog(getClass());

    protected final Class<E> entityClass;
    protected String className;

    protected EntityManager em;
    
    protected Session sn;

    protected UserInfo userInfo;

    /**
     * default constructor.
     */
    @SuppressWarnings("unchecked")
    public BaseDao() {
    	log.info("constructor BaseDao in class BaseDao begin");
    	
    	entityClass=(Class<E>)((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    	className=entityClass.getName();
    	
    	log.info("constructor BaseDao in class BaseDao end");
    }

    /**
     * return the entity class.
     * 
     * @return BaseEntity, return null if no specified the GenericParameter.
     */
    protected Class<E> getEntityClass() {
    	log.info("method getEntityClass in class BaseDao in running");

        return this.entityClass;
    }

    /**
     * 按类型和主键查询实体.
     * 
     * @param <T> t
     * @param clazz 实体类
     * @param id 主键
     * @return <T> t 查询到的实体，如果没有查询到就返回null
     */
    public abstract <T> T find(Class<T> clazz, K id);

    /**
     * 按主键查询实体，如果已经在EntityManager就直接返回，没有就去数据库查询.
     * 
     * @param id 实体主键
     * @return E 查询到的实体，如果没有查询到就返回null
     */
    public E findById(K id) {
        return this.findById(id, false);
    }

    /**
     * 按主键查询实体.
     * 
     * @param refresh true:从数据库查询，false:如果EntityManager中有直接使用，可能 会导致查询出的实体与数据库不一致。
     * @param id 实体主键
     * @return E 查询到的实体，如果没有查询到就返回null
     */
    public abstract E findById(Serializable id, boolean refresh);

    /**
     * 用于创建其它类型的实体，不受泛型限制，不会增加审计信息.
     * 
     * @param entity 要创建的实体
     */
    @Deprecated
    public abstract E persist(Object entity);

    /**
     * 创建单实体，给实体增加审计信息. 创建复合实体需要在子类重写（这个方法暂时与persist方法功能相同）
     * 
     * @param entity E
     */
    public abstract E create(E entity);

    /**
     * 先持久化entity list，再返回实体化的entity list.
     * 
     * @param entityList - entity list
     * @return persistence entity list
     */
    public List<E> createList(List<E> entityList){
    	log.info("method createList in class BaseDao begin");

		List<E> poList = new ArrayList<E>();
		for (E entity : entityList) {
			poList.add(this.create(entity));
		}

		log.info("method createList in class BaseDao end");

		return poList;
    }
    
    /**
     * 直接删除实体，不受泛型限制.
     * 
     * @param entity 要删除的实体
     */
    @Deprecated
    public abstract void remove(Object entity);

    /**
     * 直接删除实体.
     * 
     * @param entity 要删除的实体
     */
    public abstract void remove(E entity);
    
    /**
     * 按主键删除实体.
     * 
     * @param id 按组件删除
     * @return E 返回被删除的实体
     */
    public E remove(K id) {
        E e = this.findById(id);
        if (e != null) {
            this.remove(e);
        }
        return e;
    }

    /**
     * 直接删除实体List.
     * 
     * @param entity 要删除的实体List
     */
    public void removeList(List<E> entityList) {
        for (E entity : entityList) {
            this.remove(entity);
        }
    }

    /**
     * 如果EntityManager中没有缓存entity，则先根据Class和id查询entity，并缓存在EntityManager中。
     * 然后用实体化对象更新entity参数。
     * 
     * @param entity 要更新的实体，可以是vo，也可以上po
     * @return E 返回实体，更新子实体时使用。
     */
    public abstract E update(E entity);

    protected void updatePo(E entity) {
        String entityName = entity.getClass().getSimpleName();
        log.debug("start of update "+entityName+" entity (id="+entity.getId()+") ..");

//        entity.setLastActionType("MOD");
//        entity.setLastActionUser(getUserName());
//        entity.setLastActionDate(new Date());
        
        log.debug("end of update "+entityName+" entity (id="+entity.getId()+") ..");
    }

    /**
     * 查询一个表中的全表数据，主要用于global级的表.
     * 
     * @return 表中的全部数据
     */
    public List<E> queryAll() {
        return this.queryAll(false);
    }

    /**
     * 查询所有的实体对象
     * @param cacheable
     * @return
     */
    protected abstract List<E> queryAll(boolean cacheable);

    /**
     * 获得当前登录的userId，只在子类中使用.
     * 
     * @return 当前userId
     */
    protected Long getUserId() {
        return userInfo.getUserId();
    }

    protected String getUserName() {
        if(StringUtils.isBlank(userInfo.getUserName())){
            return "";
        }
        return userInfo.getUserName();
    }

    /**
     * 用于聚合实体的子实体列表更新操作.
     * 
     * @param poList 数据库中子实体list
     * @param voList 编辑后的子实体list
     */
    public void updateList(List<E> poList, List<E> voList) {

        // 1. Create new entities and update existed entites
        this.createAndUpdateList(poList, voList);

        // 2. Remove entities in poList that are not in voList
        this.removeList(poList, voList);
    }

    /**
     * Create new entities and update existed entites compared between poList and voList.
     * 
     * @param poList - current persisted entity list
     * @param voList - new entity list
     */
    private void createAndUpdateList(List<E> poList, List<E> voList) {

        for (E newEntity : voList) {
            boolean addFlag = true;
            for (E currentEntity : poList) {
                // compare id between newEntity and currentEnity
                // if the id is same, set addFlag = false to indicate update currentEnity from
                // newEntity
                if (currentEntity.getId().equals(newEntity.getId())) {
                    addFlag = false;
                    this.dataTransfer(newEntity, currentEntity);
                    this.update(currentEntity);
                    continue;
                }
            }
            if (addFlag == true) {
            	this.create(newEntity);
                poList.add(newEntity);
            }
        }
    }

    /**
     * Remove entities in poList that are not in voList.
     * 
     * @param poList - current persisted entity list
     * @param voList - new entity list
     */
    private void removeList(List<E> poList, List<E> voList) {
        List<E> removeList = new ArrayList<E>();
        for (E currentEntity : poList) {
            boolean removeFlag = true;
            for (E newEntity : voList) {
                if (currentEntity.getId().equals(newEntity.getId())) {
                    removeFlag = false;
                    continue;
                }
            }
            if (removeFlag == true) {
                removeList.add(currentEntity);
                this.remove(currentEntity);
            }
        }
        for (E entity : removeList) {
            poList.remove(entity);
        }
    }

    @SuppressWarnings("unused")
	private void listDataTransfer(List<E> pos, List<E> vos) {
        for (E vo : vos) {// source
            boolean add = true;
            for (E po : pos) {// target
                if (vo.equals(po)) {// update
                	this.dataTransfer(vo, po);
                    add = false;
                    break;
                }
            }
            if (add) {
                pos.add(vo);
            }
        }
    }

    /**
     * Copy property values from the origin bean to the destination bean for all cases where 
     * the property names are the same.
     * @param vo
     * @param po
     */
    protected void dataTransfer(BaseEntity<K> vo, BaseEntity<K> po) {
        SimpleBeanCopyUtil.copyProperties(po, vo);
    }

    /**
     * dao中需要得到em.
     * 
     * @return EntityManager
     */
    protected abstract EntityManager getEm();
    
    /**
     * dao中需要得到sn.
     * @return Session
     */
    protected abstract Session getSn();

    /**
     * query count querySingle3个方法只为子类服务.
     * 
     * 执行QueryBuilder查询，查询结果是list
     * 
     * @param queryBuilder queryBuilder
     * @return List<Object>
     */
    protected abstract List<E> query(QueryBuilder queryBuilder);

    /**
     * queryVO for query object is VO.
     * 
     * return voList;
     * 
     * @param queryBuilder queryBuilder
     * @return List
     */
    protected abstract List<E> queryVO(QueryBuilder queryBuilder);

    /**
     * 执行QueryBuilder查询.
     * 
     * @param queryBuilder queryBuilder
     * @return Long 查询总数
     */
    protected abstract Long count(QueryBuilder queryBuilder);

    /**
     * 执行QueryBuilder查询，查询单个实体.
     * 
     * @param queryBuilder queryBuilder
     * @return Object 单个实体，查询不到返回null
     */
    protected abstract E querySingle(QueryBuilder queryBuilder);

    /**
     * 执行QueryBuilder查询，查询单个实体. forceReturnSingle is true, force return the first entity; otherwise
     * throw exception.
     * 
     * @param queryBuilder queryBuilder
     * @param forceReturnSingle boolean
     * @return Object 单个实体，查询不到返回null
     */
    protected abstract E querySingle(QueryBuilder queryBuilder, boolean forceReturnSingle);

    /**
     * batch execute update by JPQL or Native sql in QueryBuilder.
     * 
     * @param queryBuilder - QueryBuilder
     * @return the number of entities updated
     */
    protected abstract int batchUpdate(QueryBuilder queryBuilder);

    /**
     * batch execute remove by JPQL or Native sql in QueryBuilder.
     * 
     * @param queryBuilder - QueryBuilder
     * @return the number of entities deleted
     */
    protected int batchRemove(QueryBuilder queryBuilder) {
        return this.batchUpdate(queryBuilder);
    }

}
